Redux 简单实现 系列文章(四):react-redux 的原理。
react-redux 究竟是什么
前面我们已经介绍了 Redux 的基本原理,但似乎还没有把 Redux 和 React 两者联系起来。React 负责视图层的构造,Redux 负责数据流的处理。我们希望可以通过视图层的操作触发数据的更新,同时也希望数据变化的时候能够触发视图层的更新,这样就形成了一个完美的闭环。
在介绍 react-redux 之前,我们先自己想想,我要如何在组件里获取 store 里的数据?当然是 store.getState()。如何在 state 变化的时候收到更新通知?当然是 store.subscribe()。但每个组件都这样写显然是极为繁琐而不清晰的。幸好,我们有 react-redux。
react-redux 的核心功能只有 2 个:「Provider 组件」和「connect 方法」。下面我们来逐一介绍。
Provider 组件
我们先想想,整个 React 项目里,那么多组件需要使用 store,哪种方式是最适合传递的呢?答案是:context。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// react-redux 4.x
export default class Provider extends Component {
getChildContext() {
return { store: this.store }
}
constructor(props, context) {
super(props, context)
this.store = props.store
}
render() {
return Children.only(this.props.children)
}
}
在 Provider 组件中,我们在 constructor 里获取到了 store 对象,并存放在了组件实例上。同时,通过 getChildContext 方法将其放入 context 中。这样,子组件就能通过 context 对象获取到 store。
目前为止,我们只解决了 store 的传递问题。子组件可以通过 context 来获取到最顶层 Provider 里的 store。但,如何在 state 变化的时候获取到更新通知呢?接下来,我们介绍 connect。
connect 方法
Provider 组件提供了直接访问 store 的基础,而 connect 方法则是真正连接了 Redux store 和 React 组件的工具。这里引用简化版的源码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44export default function connect(
mapStateToProps,
mapDispatchToProps,
mergeProps,
options = {}
) {
// ...
return function wrapWithConnect(WrappedComponent) {
class Connect extends Component {
constructor(props, context) {
super(props, context)
this.version = version
this.store = props.store || context.store
const storeState = this.store.getState()
this.state = { storeState }
}
render() {
this.renderedElement = createElement(WrappedComponent,
this.mergedProps
)
return this.renderedElement;
}
trySubscribe() {
if (shouldSubscribe && !this.unsubscribe) {
this.unsubscribe = this.store.subscribe(this.handleChange.bind(this))
this.handleChange()
}
}
handleChange() {
const storeState = this.store.getState()
this.setState({ storeState })
}
componentDidMount() {
this.trySubscribe()
}
}
return hoistStatics(Connect, WrappedComponent)
}
}
以上就是简化版的 connect。这里只抽了跟订阅相关的部分,如果觉得看得逻辑不通的话可以直接看 完整源码。
我们看到,connect 返回了一个接收外部传入的业务组件的方法,这个组件最终被用于 Connect 组件渲染的时候进行渲染。Connect 组件初始化的时候,从 context 里拿到了从 Provider 传下来的 store。在 componentDidMount 时,进行了对 store 的订阅。这样一来,当 state 发生更新的时候,我们就能收到通知,调用 handleChange 来更新 Connect 组件的 state,触发组件的更新。
当然,还有关于 mapStateToProps、mapDispatchToProps 等等的逻辑,由于这次只介绍订阅相关的知识,就不在这里进行展开了。
相关链接
Redux 简单实现(一):状态管理器
Redux 简单实现(二):多文件协作
Redux 简单实现(三):中间件
参考资料: